package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.UserSearchDto;
import com.heima.model.search.vo.SearchArticleVo;
import com.heima.model.threadlocal.AppThreadLocalUtils;
import com.heima.model.user.pojo.ApUser;
import com.heima.search.service.ApUserSearchService;
import com.heima.search.service.ArticleSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @作者 itcast
 * @创建日期 2021/6/8 10:34
 **/
@Service
@Slf4j
public class ArticleSearchServiceImpl implements ArticleSearchService {

    public static final String INDEX_NAME = "app_info_article";//常量索引库


    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Autowired
    ApUserSearchService apUserSearchService;

    @Override
    public ResponseResult search(UserSearchDto userSearchDto) {
        // 0. 检查参数 (关键字)
        if (StringUtils.isBlank(userSearchDto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"搜索的关键词不能为空");
        }
        int pageSize = userSearchDto.getPageSize();
        if (pageSize==0){
            pageSize = 10;//设置默认条数
        }
        //搜索首页   记录搜索的历史记录
        if (userSearchDto.getFromIndex()==0){//首页
            //首页才记录  搜索的关键词
            ApUser user = AppThreadLocalUtils.getUser();
            //提前获取登录用户的id
            //异步执行   属于不同线程   获取不到
            userSearchDto.setEntryId(user==null?null:user.getId());//当前登录用户的id

            apUserSearchService.insert(userSearchDto);//异步执行

        }

        //1.构建搜索请求
        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);//将要查的索引库   创建成一个常量

        //SearchSourceBuilder   构建所有的请求参数
        SearchSourceBuilder builder = new SearchSourceBuilder();//整合  所有请求条件

        //构建query请求
        //  构建  bool  请求参数对象     must   should   mustnot
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//QueryBuilders  通用请求工具类
        //通过must  加入  按照match  分词          按照索引库  的设计好的字段  查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title",userSearchDto.getSearchWords()));
        //filter   不参与得分的计算     rangeQuery  表示  范围        小于最小时间   查询更多
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("publishTime").lt(userSearchDto.getMinBehotTime()));

        builder.query(boolQueryBuilder);

        //构建highlight  请求
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        //设置 title  字段高亮
        highlightBuilder.field("title");
        //前置标签
        highlightBuilder.preTags("<span style='color:red'>");
        //后置标签
        highlightBuilder.postTags("</span>");


        builder.highlighter(highlightBuilder);

        //构建sort排序   设置按照发布时间  降序排序
        builder.sort("publishTime",SortOrder.DESC);//数据按照索引库中的发布时间  字段  降序排序

        //设置分页参数  from(0)(不起作用)  +size 按照时间排序  分页   默认起始是0

        builder.from(0);
        builder.size(pageSize);


        searchRequest.source(builder);//整合所有请求条件

        //调用客户端  执行搜索条件   得到 SearchResponse  响应对象

        List<SearchArticleVo> searchArticleVoList=new ArrayList<>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //解析响应的结果
            //得到命中的结果
            SearchHits hits = searchResponse.getHits();
            //得到文档结果列表
            SearchHit[] docArr = hits.getHits();
            for (SearchHit doc : docArr) {
                //得到文章文档的json字符串
                String articleJson = doc.getSourceAsString();
                //封装文章实体类
                SearchArticleVo searchArticleVo = JSON.parseObject(articleJson, SearchArticleVo.class);//封装返回值
                //获取高亮结果
                Map<String, HighlightField> highlightFields = doc.getHighlightFields();
                //title的高亮结果
                HighlightField highlightField = highlightFields.get("title");
                //拼接所有的高亮片段    得到完整的高亮处理结果          所 有的高亮结果数组   拼接
                String highlightResult = StringUtils.join(highlightField.getFragments());
                searchArticleVo.setTitle(highlightResult);//取代原来的title写法

                //最后封装成一个list   返回结果
                searchArticleVoList.add(searchArticleVo);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"搜索出现异常");
        }

        return ResponseResult.okResult(searchArticleVoList);
    }















    @Override//保存文章  就是往索引库中添加数据
    public void saveArticle(SearchArticleVo article) {
        //往索引库中添加数据
        IndexRequest indexRequest = new IndexRequest(INDEX_NAME);//索引库
        indexRequest.id(String.valueOf(article.getId()));
        indexRequest.source(JSON.toJSONString(article), XContentType.JSON);//source  把文章内容转化为json
        try {
            restHighLevelClient.index(indexRequest,RequestOptions.DEFAULT);//调用客户端保存,给一个默认的选项
        } catch (IOException e) {
            e.printStackTrace();
            log.error("同步索引库 文档内容： {}   添加文档失败  : {} ",article,e.getMessage());//消费者  不能抛
        }
    }




    @Override
    public void delete(Long articleId) {
        DeleteRequest request = new DeleteRequest(INDEX_NAME,String.valueOf(articleId));//操作  es索引库  deleteRequest
        //deleteIndex   删除索引库
        try {
            restHighLevelClient.delete(request,RequestOptions.DEFAULT);//调用es客户端   来操作
        } catch (IOException e) {
            e.printStackTrace();
            log.error("同步索引库 文档ID： {}   删除文档失败  : {} ",articleId,e.getMessage());


            //在索引库中是真的删除       数据库中   自媒体端是真删   article是假删   关联文章的delete字段改为1
        }
    }
}
